import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Supplier;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;


//左下方(红队),右上方(蓝队)
public class GameFrame extends JFrame {
	// 窗口尺寸
	static int windowWidth = 1198;
	static int windowHeight = 891;
	static int zoomOutTimes = 5;

	//第一层绘图缓冲区，用于画背景，画玩家，画怪
	private Image buffer;  // 双缓冲图像
	private Graphics bufferGraphics;  // 双缓冲图像的绘图上下文
	//第二层绘图缓冲，用于画地图，画缩略地图，玩家界面等等
	private Image buffer2;  // 双缓冲图像
	private Graphics bufferGraphics2;  // 双缓冲图像的绘图上下文

	//游戏背景
	Background background; //黑白的小地图
	Background background2; //彩色的大地图

	//用于获取像素值
	static public BufferedImage backgroundBufferedImage;
	static public BufferedImage backgroundBufferedImage2;

	static Map map;
	//玩家
	Player player;
	Player player2;

	int startAngle;
	Coord mousePosition;
	//小兵
	//Vector<Minion> minions;

	//打印日志工具，可以看到打印时当前类，方法，行，比便方便，sout不能看到这些信息不太方便
	Logger logger = MyLogger.getLogger();

	boolean isVK_A_Pressed; //jump
	boolean isVK_S_Pressed;
	boolean isVK_D_Pressed;
	boolean isVK_F_Pressed;

	//游戏计时器
	long timer;

	//左下方(红队)所有玩家，塔，小兵
	ArrayList<Unit> redTeam;
	public static int redTeamUnitID;
	public static int blueTeamUnitID;

	//右上方(蓝队)所有玩家，塔，小兵
	ArrayList<Unit> blueTeam;

	public GameFrame() {
		// 设置尺寸
		setSize(windowWidth, windowHeight);

		// 窗口居中
		setLocationRelativeTo(null);

		// 关闭事件
		setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

		// 用户不能调整窗口大小
		setResizable(true);

		// 标题
		setTitle("王者有点荣耀");

		// 窗口可见
		setVisible(true);

		//map = new Map();
		prepareBufferForDisplay();

		player = new Player(Team.RED, 178, 682, 1000, 100, 120, 60, 6);
		player2 = new Player(Team.BLUE, 602, 394, 100, 100, 120, 60, 6);


		redTeam = new ArrayList<>();
		blueTeam = new ArrayList<>();

		isVK_A_Pressed = false;
		mousePosition = new Coord(0,0);
		redTeam.add(player);

		//左下方，
		redTeam.add(new DefenseTower(Team.RED, 372, 758, 120, 2));
		redTeam.add(new DefenseTower(Team.RED, 530, 764, 120, 2));
		redTeam.add(new DefenseTower(Team.RED, 799, 766, 120, 2));
		redTeam.add(new DefenseTower(Team.RED, 226, 730, 120, 2));
		redTeam.add(new DefenseTower(Team.RED, 325, 647, 120, 2));
		redTeam.add(new DefenseTower(Team.RED, 416, 598, 120, 2));
		redTeam.add(new DefenseTower(Team.RED, 490, 532, 120, 2));
		redTeam.add(new DefenseTower(Team.RED, 167, 615, 120, 2));
		redTeam.add(new DefenseTower(Team.RED, 176, 518, 120, 2));
		redTeam.add(new DefenseTower(Team.RED, 177, 317, 120, 2));


		//右上方，
		blueTeam.add(new DefenseTower(Team.BLUE, 1020, 606, 120, 10));
		blueTeam.add(new DefenseTower(Team.BLUE, 1024, 420, 120, 10));
		blueTeam.add(new DefenseTower(Team.BLUE, 1012, 314, 120, 10));
		blueTeam.add(new DefenseTower(Team.BLUE, 871, 260, 120, 10));
		blueTeam.add(new DefenseTower(Team.BLUE, 767, 334, 120, 10));
		blueTeam.add(new DefenseTower(Team.BLUE, 691, 395, 120, 10));
		blueTeam.add(new DefenseTower(Team.BLUE, 957, 200, 120, 10));
		blueTeam.add(new DefenseTower(Team.BLUE, 811, 175, 120, 10));
		blueTeam.add(new DefenseTower(Team.BLUE, 654, 165, 120, 10));
		blueTeam.add(new DefenseTower(Team.BLUE, 384, 178, 120, 10));
		blueTeam.add(player2);

		addKeyListener();
		addMouseListener();
		updateWindow();
	}

	static int getRedTeamUnitID(Team team)
	{
		return redTeamUnitID++;
	}

	void prepareBufferForDisplay()
	{
		try {
			//5984 x 4452 攻击图片
			//backgroundBufferedImage = ImageIO.read(new File("img/Map.jpg"));
			backgroundBufferedImage = ImageIO.read(new File("img/Map_zoom_out_5_2.jpg"));
			backgroundBufferedImage2 = ImageIO.read(new File("img/Map_big.jpg"));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		logger.info("img/Map.jpg width: " + backgroundBufferedImage.getWidth() + " height:" + backgroundBufferedImage.getHeight());

		buffer = createImage(backgroundBufferedImage.getWidth(), backgroundBufferedImage.getHeight());
		bufferGraphics = buffer.getGraphics();

		map = new Map(backgroundBufferedImage, buffer);

		buffer2 = createImage(backgroundBufferedImage.getWidth(), backgroundBufferedImage.getHeight());
		bufferGraphics2 = buffer2.getGraphics();

		background = new Background(800,  800,0,0, 30);
		background.image = backgroundBufferedImage;
		background2 = new Background(800,  800,0,0, 30);
		background2.image = backgroundBufferedImage2;
	}

	void addMouseListener()
	{
		addMouseMotionListener(new MouseMotionAdapter() {
			@Override
			public void mouseDragged(MouseEvent e) {
				super.mouseDragged(e);
			}

			@Override
			public void mouseMoved(MouseEvent e) {
				super.mouseMoved(e);
				int x = e.getX();
				int y = e.getY();
				logger.info(e.getX() + ", " + e.getY());
				mousePosition.setX(x);
				mousePosition.setY(y);

				float deltax =Math.abs(x - player.x);
				float deltay =Math.abs(y - player.y);
				final double sqrt = Math.sqrt(deltax * deltax + deltay * deltay);
				logger.info(String.valueOf(Math.asin(deltay/sqrt)));
				startAngle = 0;
				if (player.x <= x && player.y > y) //OK 右上
				{
					startAngle = (int) ((double)180 / Math.PI * Math.asin(deltay/sqrt) - 30);
				}
				else if (player.x > x && player.y >= y) //OK 左上
				{
					startAngle = (int) (( 180 - 180 / Math.PI * Math.asin(deltay/sqrt))) - 30;
				}
				else if (player.x >= x && player.y < y) //左下
				{
					startAngle = (int) (( 180 + 180 / Math.PI * Math.asin(deltay/sqrt))) - 30;
				}
				else if (player.x < x && player.y< y) //右下
				{
					startAngle = (int) ( 360 - 180 / Math.PI * Math.asin(deltay/sqrt)) - 30;
				}

				//bufferGraphics.setColor(new Color(255,0,0));
				//bufferGraphics.fillArc(player.x  - 300/2, player.y   - 300/2, 300, 300, startAngle, 60);

				if (e.getX() > windowWidth-30)
				{
					background.move(background.step, 0);
				}

				if (e.getX() < 30)
				{
					background.move(-background.step, 0);
				}

				if (e.getY() > windowHeight-30)
				{
					background.move(0, background.step);
				}

				if (e.getY() < 80)
				{
					background.move(0, -background.step);
				}
			}
		});

		addMouseListener(new MouseListener() {
			@Override
			public void mouseClicked(MouseEvent e) {

				logger.info(String.valueOf(Map.data[ e.getY()][e.getX() ]));
				logger.info("mouseClicked x: " + e.getX() + " ,y: " + e.getY());
				int x = e.getX();
				int y = e.getY();

				if (isVK_A_Pressed)
				{
					player.x = e.getX();
					player.y = e.getY();
					isVK_A_Pressed = false;
					player.pathToTargetList.clear();
					return;
				}


				if (isVK_S_Pressed)
				{
					float deltax =Math.abs(x - player.x);
					float deltay =Math.abs(y - player.y);
					final double sqrt = Math.sqrt(deltax * deltax + deltay * deltay);
					logger.info(String.valueOf(Math.asin(deltay/sqrt)));
					bufferGraphics.fillArc(player.x  - 300/2, player.y   - 300/2, 300, 300, (int) ((double)180 / Math.PI * Math.asin(deltay/sqrt) - 30), 60);
					isVK_S_Pressed = false;
					return;
				}


				if (Map.data[e.getY()][e.getX()] == MapUnit.IDLE)
				{
					player.updateTarget(new Coord(e.getX(), e.getY()));
				}

				int xReductionFactor = 5984 / 200;
				int yReductionFactor = 4452 / 200;
				if (x > 20 && y > 20 && x < 20 + 200 && y < 20  + 200)
				{

					background.startx = (x - 20 - windowWidth / xReductionFactor / 2) * xReductionFactor;
					background.startx /= zoomOutTimes;
					background.starty = (y - 20 - windowHeight / yReductionFactor / 2) * yReductionFactor;
					background.starty /= zoomOutTimes;
				}
			}

			@Override
			public void mousePressed(MouseEvent e) {
			}

			@Override
			public void mouseReleased(MouseEvent e) {
			}

			@Override
			public void mouseEntered(MouseEvent e) {
			}

			@Override
			public void mouseExited(MouseEvent e) {
			}
		});
	}

	void addKeyListener()
	{
		this.addKeyListener(new KeyAdapter() {
			@Override
			public void keyTyped(KeyEvent e) {
			}

			@Override
			public void keyPressed(KeyEvent e) {
				super.keyPressed(e);
				int keyCode = e.getKeyCode();
				logger.info(String.valueOf(keyCode));
				int rgb = backgroundBufferedImage.getRGB(player.x, player.y);
				int red = (rgb >> 16) & 0xff;
				int green = (rgb >> 8) & 0xff;
				int blue = rgb & 0xff;

				logger.info("Player at (" + player.x + "," + player.y + "): RGB(" + red + "," + green + "," + blue + ")");
				if (green < 120)
				{
					//墙壁
					//return;
				}
				switch (keyCode)
				{
					//case KeyEvent.VK_D:
						//player.move(player.step, 0);
						//background.move(player.step, 0);
						//break;

					case KeyEvent.VK_A: //JUMP
						isVK_A_Pressed = true;
						//player.move(-player.step, 0);
						//background.move(-player.step, 0);
						break;

					case KeyEvent.VK_S: //Fire heading direction.
						isVK_S_Pressed = true;
						//player.move(0, player.step);
						//background.move(0, player.step);

						break;

					case KeyEvent.VK_D:
						isVK_D_Pressed = true;
						//player.move(0, player.step);
						//background.move(0, player.step);

						break;
					case KeyEvent.VK_F:
						isVK_F_Pressed = true;
						//player.move(0, player.step);
						//background.move(0, player.step);

						break;
					case KeyEvent.VK_W:
						//player.move(0, -player.step);
						//background.move(0, -player.step);
						break;
				}
			}

			@Override
			public void keyReleased(KeyEvent e) {
				super.keyReleased(e);
			}
		});
	}

	//把地图，兵线，玩家，防御塔画在第一层缓冲区上
	void Buffer1Prepare()
	{
		int x = player.x;
		int y = player.y;

		//第一种办法：先取背景，把英雄，怪画到一张大图，然后基于大图取一个矩形显示

		//第二种办法：直接从背景取窗口要显示的区域，然后根据英雄或者怪是否在要显示的窗口里，计算后，再显示

		//画背景
		bufferGraphics.drawImage(background2.image, 0, 0,
				backgroundBufferedImage.getWidth(), backgroundBufferedImage.getHeight(), null);

		//bufferGraphics.drawImage(background.image, 0, 0,
		//		backgroundBufferedImage.getWidth(), backgroundBufferedImage.getHeight(), null);

		final int width = backgroundBufferedImage.getWidth();
		final int height = backgroundBufferedImage.getHeight();

//		bufferGraphics.setColor(new Color(0, 0, 0));
//		for (int i = 0; i < height; i++) {
//			for (int j = 0; j < width; j++) {
//				int rgb = backgroundBufferedImage.getRGB(j, i);
//				int red = rgb & 0xff0000 >> 16;
//				if (red < 20) {
//					bufferGraphics.drawLine(j, i, j, i);
//				}
//			}
//		}

		//准备画所有防御塔攻击范围
		for(Unit unit: redTeam)
		{
			bufferGraphics.setColor(new Color(255,0,0));
			unit.paint(bufferGraphics);
			unit.move();
		}

		for(Unit unit: blueTeam)
		{
			bufferGraphics.setColor(new Color(0,0,255));
			unit.paint(bufferGraphics);
			unit.move();
		}

		//如果小兵跑出指定区域就删除，不然小兵会非常多
		Iterator<Unit> iterator = redTeam.iterator();
		while (iterator.hasNext())
		{
			Unit unit= (Unit)iterator.next();
			if (unit instanceof Minion)
			{
				Minion minion = (Minion)unit;

				//下路兵，如果走到一定高度就删除，如果有双方兵，如果血量少于0就删除
				if (minion.line == MinionLine.DOWN_LINE && minion.y < minion.removeY)
					iterator.remove();

				if (minion.line == MinionLine.MIDDLE_LINE && minion.x > 800)
					iterator.remove();

				if (minion.line == MinionLine.UPPER_LINE && minion.x > 800)
					iterator.remove();
			}
		}

		iterator = blueTeam.iterator();
		while (iterator.hasNext())
		{
			Unit unit= (Unit)iterator.next();
			if (unit instanceof Minion)
			{
				Minion minion = (Minion)unit;

				//下路兵，如果走到一定高度就删除，如果有双方兵，如果血量少于0就删除
				if (minion.line == MinionLine.DOWN_LINE && minion.x < minion.removeY)
					iterator.remove();

				if (minion.line == MinionLine.MIDDLE_LINE && minion.x < 260)
					iterator.remove();

				if (minion.line == MinionLine.UPPER_LINE && minion.y > 600)
					iterator.remove();
			}
		}

		ArrayList<Unit> blueNeedToRemove = new ArrayList<>();
		ArrayList<Unit> redNeedToRemove = new ArrayList<>();

		final Iterator<Unit> iteratorRed = redTeam.iterator();

		//小兵攻击范围检测，红兵碰到蓝兵，就会攻击蓝兵
		while (iteratorRed.hasNext())
		{
			Unit unitRed = iteratorRed.next();

			final Iterator<Unit> iteratorBlue = blueTeam.iterator();

			//小兵攻击范围检测，红兵碰到蓝兵，就会攻击蓝兵
			while (iteratorBlue.hasNext())
			{
				Unit unitBlue = iteratorBlue.next();

				if (unitRed.attack != null) //正在攻击目标
				{
					unitRed.attack(bufferGraphics);

					if (unitRed.attack.target.hpLeft <= 0) //红色的攻击目标，就是蓝色了
					{
						unitRed.state = UnitState.Move;
						blueNeedToRemove.add(unitRed.attack.target);
					}
				}

				if (unitBlue.attack != null) //正在攻击目标
				{
					unitBlue.attack(bufferGraphics);

					if (unitBlue.attack.target.hpLeft <= 0)
					{
						unitBlue.state = UnitState.Move;
						redNeedToRemove.add(unitBlue.attack.target);
					}
				}

                if (encounter(unitRed, unitBlue)) //没有目标攻击，有新的目标进入攻击范围
				{
					unitRed.state = UnitState.Attack;
					unitRed.attack = new Attack(unitRed, unitBlue, 20, 5);

					//logger.info(String.valueOf(distance(unitRed,unitBlue)));
					//logger.info(unitBlue.toString());
                }
				if (encounter(unitBlue, unitRed)) //没有目标攻击，有新的目标进入攻击范围
				{
					unitBlue.state = UnitState.Attack;
					unitBlue.attack = new Attack(unitBlue, unitRed, 20, 5);

					//logger.info(unitBlue.toString());
				}

			}
		}

		//生成小兵，这个if判断很精髓，大概每60个timer生成一波兵，一次生成3个
		if (timer  % 60 <  3 )
		{
			redTeam.add(new Minion(Team.RED, MinionLine.UPPER_LINE));
			redTeam.add(new Minion(Team.RED, MinionLine.MIDDLE_LINE));
			redTeam.add(new Minion(Team.RED, MinionLine.DOWN_LINE));

			blueTeam.add(new Minion(Team.BLUE, MinionLine.UPPER_LINE));
			blueTeam.add(new Minion(Team.BLUE, MinionLine.MIDDLE_LINE));
			blueTeam.add(new Minion(Team.BLUE, MinionLine.DOWN_LINE));

		}

		for (int i = 0; i < Map.height; i++) {
			for (int j = 0; j < Map.width; j++) {
				if (Map.data[i][j] != MapUnit.IDLE && Map.data[i][j] != MapUnit.BAR )
					bufferGraphics.fillRect(j, i,3,3);
			}
		}
		bufferGraphics2.drawImage(buffer,0,0, windowWidth, windowHeight,
				background.startx , background.starty ,background.startx + windowWidth,background.starty + windowHeight,
				null);

		getGraphics().drawImage(buffer2,0,0, this);

		if(player.hpLeft <= 0)
		{
			logger.info(player.team +" is dead, and will revive at home.");
			player = new Player(Team.RED, 178, 682, 1000, 100, 120, 60, 6);
			redTeam.add(player);
		}

		bufferGraphics.setColor(new Color(255,0,0));
		bufferGraphics.fillArc(player.x  - 200/2, player.y   - 200/2, 200, 200, startAngle, 60);

		bufferGraphics.setColor(new Color(0,0,0));
		bufferGraphics.drawLine(player.x, player.y, mousePosition.getX(),mousePosition.getY());
		for (Unit unit: blueNeedToRemove)
			blueTeam.remove(unit);

		for (Unit unit: redNeedToRemove)
			redTeam.remove(unit);
	}

	//检测是否在相互的攻击范围
	boolean encounter(Unit unit1, Unit unit2)
	{
		int x = Math.abs(unit1.x - unit2.x);
		int y = Math.abs(unit1.y - unit2.y);

		final double v = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
		final double w = unit1.attackRadius;

        return v < w;
    }

	double distance(Unit unit1, Unit unit2)
	{
		int x = Math.abs(unit1.x - unit2.x);
		int y = Math.abs(unit1.y - unit2.y);

		final double v = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
		final double w = unit1.attackRadius;

		return v;
	}
	//基于缓冲区1画用于展示到窗口的部分
	void Buffer2Prepare()
	{
		int x = player.x;
		int y = player.y;

		bufferGraphics2.drawImage(buffer,0,0, windowWidth, windowHeight,
				background.startx , background.starty ,background.startx + windowWidth,background.starty + windowHeight,
				null);

		//画小地图
		bufferGraphics2.drawImage(buffer,0,0,200,200,null);

		int xReductionFactor = 5984 / 200;
		int yReductionFactor = 4452 / 200;

		//画当前显示窗口在地图中的位置，就是地图里的小矩形和当前玩家在地图上的位置，一个红色小矩形
		bufferGraphics2.setColor(new Color(255, 0, 0));
		bufferGraphics2.drawRect(background.startx / xReductionFactor,background.starty / yReductionFactor,windowWidth / xReductionFactor,windowHeight / yReductionFactor);
		bufferGraphics2.fillRect(x / xReductionFactor,20 + y / yReductionFactor,5,5);

		//模拟技能冷却
		bufferGraphics2.setColor(new Color(0,  0,255));
		bufferGraphics2.fillArc(950, 850, 50, 50, 0, (int) (timer % 11 * 36));
		bufferGraphics2.fillArc(1000, 800, 50, 50, 0, (int) ((timer + 2) % 8 * 52));
		bufferGraphics2.fillArc(1100, 750, 50, 50, 0, (int) ((timer + 5) % 11 * 36));
		//logger.info(redTeam.size() + " " + blueTeam.size());
	}

	//窗口画面刷核心方法
	void updateWindow()
	{
		while (true)
		{
			timer++;

			Buffer1Prepare();
			Buffer2Prepare();

			getGraphics().drawImage(buffer2,0,0, this);

			try {
				Thread.sleep(60);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	boolean isWall(int x, int y)
	{
		int rgb = backgroundBufferedImage.getRGB(x, y);
		int green1 = (rgb >> 8) & 0xff;
		if (green1 > 120)
		{
			//墙壁
			return true;
		}

		return true;
	}

	// main方法
	public static void main(String[] args) {
		new GameFrame();
	}
}